不要單純地以為,從CSS改字型可以妝點<Text />
。
但照著官方文件教的,在<Text />
裡寫上font={fontUrl},卻會遇到Cannot destructure property 'ascender' of 'fontObj' as it is undefined.
我想過幾個原因。一開始以為是<Text />
不支援woff2所致,但換成woff結果相同。插曲是我想直接外連github上的ttf時,遇到CORS問題,哈。所以也試過把字型檔傳到codesandbox上,甚至另闢蹊徑,嘗試<Text3D />
的useLoader搭配typeface.json等怪招……最後發現是資料格式作祟。
只要一直用陣列,寫成單純map,別麻煩split就沒問題了。
中文內容這邊,借用別具魅力的西幽角色「萬軍破」的詩號。28個字繞一圈蠻剛好的。
私心希望文字是好讀的由左至右,遂將angleStep那邊從2改成-2。
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Text } from "@react-three/drei";
export default function Ring({ text = "", radius, height, segments }) {
const ref = useRef();
useFrame(() => {
ref.current.rotation.x += 0.01;
ref.current.rotation.y += 0.01;
ref.current.rotation.z += 0.01;
});
const textPositions = [];
const angleStep = (-2 * Math.PI) / text.length;
for (let i = 0; i < text.length; i++) {
const angle = i * angleStep;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
textPositions.push({ x, z });
}
return (
<group ref={ref}>
<mesh>
<cylinderGeometry args={[radius, radius, height, segments]} />
<MeshTransmissionMaterial
thickness={3}
roughness={0.1}
distortion={0.5}
/>
</mesh>
{text.map((char, index) => (
<Text
key={index}
position={[textPositions[index].x, 0, textPositions[index].z]}
rotation={[0, -angleStep * index + Math.PI / 2, 0]}
font="https://fonts.cdnfonts.com/s/93375/ZenKakuGothicNew-Light.woff"
fontSize={0.3}
lineHeight={1}
letterSpacing={0.02}
color="white"
textAlign="center"
>
{char}
</Text>
))}
</group>
);
}
最後我決定把中心的圓柱也改為別的量體。
挑來挑去覺得符合「繞一圈條件」的<torusGeometry />
蠻順眼,或許是因為甜甜圈外型,喚起我在《媽的多重宇宙》看到the Everything Bagel的震撼了吧。
替換的過程中基本上不會有大狀況,只要新的props有對應到就行。
為了貼合torus不同的半徑,我也把textPositions 乘上了1.5。
再送上一個小叮嚀:要讓站立的torus於虛擬空間中斜倚或平躺,可不是在<torusGeometry />
裡附上rotate就能了事,而應當在mesh套用旋轉才是正解。
耶,成品變得像顆繞有星環的天體了。
//App.js
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import Ring from "./ring";
import "./styles.css";
export default function App() {
return (
<main>
<Canvas>
<OrbitControls />
<Ring
radius={2}
tube={1}
radialSegments={16}
tubularSegments={28}
text={[
"招",
"汚",
"負",
"蔑",
"清",
"名",
"礙",
"豈",
"識",
"初",
"心",
"未",
"曾",
"改",
"明",
"朝",
"浴",
"血",
"不",
"枉",
"義",
"吾",
"之",
"大",
"道",
"吾",
"主",
"宰",
]}
/>
</Canvas>
</main>
);
}
//ring.js
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Text } from "@react-three/drei";
export default function Ring({
text = "",
radius,
tube,
radialSegments,
tubularSegments,
}) {
const ref = useRef();
useFrame(() => {
ref.current.rotation.x += 0.01;
ref.current.rotation.y += 0.01;
ref.current.rotation.z += 0.01;
});
const textPositions = [];
const angleStep = (-2 * Math.PI) / text.length;
for (let i = 0; i < text.length; i++) {
const angle = i * angleStep;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
textPositions.push({ x, z });
}
return (
<group ref={ref}>
<mesh rotation={[Math.PI / 4, 0, 0]}>
<torusGeometry args={[radius, tube, radialSegments, tubularSegments]} />
<MeshTransmissionMaterial
thickness={1.5}
roughness={0.1}
distortion={0.5}
/>
</mesh>
{text.map((char, index) => (
<Text
key={index}
position={[
textPositions[index].x * 1.5,
0,
textPositions[index].z * 1.5,
]}
rotation={[0, -angleStep * index + Math.PI / 2, 0]}
font="https://fonts.cdnfonts.com/s/93375/ZenKakuGothicNew-Light.woff"
fontSize={0.3}
lineHeight={1}
letterSpacing={0.02}
color="white"
textAlign="center"
>
{char}
</Text>
))}
</group>
);
}